package com.zipeng.spring;

import com.zipeng.spring.annotation.Autowired;
import com.zipeng.spring.annotation.Component;
import com.zipeng.spring.annotation.ComponentScan;
import com.zipeng.spring.annotation.Scope;
import com.zipeng.spring.core.BeanDefinition;
import com.zipeng.spring.myinterface.BeanNameAware;
import com.zipeng.spring.myinterface.BeanPostProcessor;
import com.zipeng.spring.myinterface.InitializingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class MyApplicationContext {

    /**
     * 容器配置类
     */
    private Class configClass;
    /**
     * 存储 单例Bean 的容器
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    /**
     * 存储 Bean定义 的容器
     */
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    /**
     * 存储 beanPostProcessor
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public MyApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 1. 根据配置类，扫描组件，生成 BeanDefinition
        scan(configClass);
        // 2. 根据 beanDefinitionMap 把所有的单列 Bean 都实例化到 单例池
        for(String beanName: beanDefinitionMap.keySet()){
            // 2.1 获取 beanDefinition
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 2.2 判断是否为单例
            if(beanDefinition.getScope().equals("singleton")){
                // 2.2.1 创建 bean
                Object bean = createBean(beanName, beanDefinition);
                // 2.2.2 把bean放入单例池
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition){
        // 1. 获取对应类对象
        Class clazz = beanDefinition.getClazz();
        try {
            // 2. 实例化对象
            Object instance = clazz.newInstance();
            // 3. 依赖注入
            for (Field declaredField : clazz.getDeclaredFields()) {
                // 3.1 判断该字段是否需要注入
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    // 3.1.1 获取 bean
                    Object bean = getBean(declaredField.getName());
                    // 3.1.2 设置该字段可访问
                    declaredField.setAccessible(true);
                    // 3.1.3 注入
                    declaredField.set(instance, bean);
                }
            }
            // 4. 判断是否实现了BeanNameAware接口
            if(instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            // 5. 初始化前：调用 postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 6. 初始化
            if(instance instanceof InitializingBean){
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // 7. 初始化后：调用 postProcessAfterInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据配置类，扫描组件，生成 BeanDefinition
     * @param configClass
     */
    private void scan(Class configClass) {
        // 1.解析配置类
        // 1.1 解析ComponentScan注解
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        // 1.2 获得扫描路径 path
        String path = componentScanAnnotation.value();
        // 1.3 获取扫描路径下的 Component
        // 1.3.1 获取类加载器
        ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
        // 1.3.2 获取资源的URL
        URL resource = classLoader.getResource(path.replace(".", "/"));
        // 1.3.3 把URL转为一个文件
        File file = new File(resource.getFile());
        // 1.3.4 判断是否为一个目录
        if(file.isDirectory()){
            // 1.3.4.1 获取目录下的所有文件
            File[] files = file.listFiles();
            // 1.3.4.2 遍历数组进行逐个加载
            for(File item: files){
                // 1.3.4.2.1 构造类的全限定名
                String className = path + "." + item.getName().substring(0, item.getName().lastIndexOf("."));
                try {
                    // 1.3.4.2.2 使用类加载器进行加载
                    Class<?> aClass = classLoader.loadClass(className);
                    // 1.3.4.2.3 判断该类是否存在 Component 注解
                    if(aClass.isAnnotationPresent(Component.class)){

                        // 1.3.4.2.3.0 判断当前类对象是否实现了BeanPostProcessor接口
                        if(BeanPostProcessor.class.isAssignableFrom(aClass)){
                            BeanPostProcessor instance = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(instance);
                        }
                        // 1.3.4.2.3.1 获取 Component 注解
                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        // 1.3.4.2.3.2 获取 beanName
                        String beanName = componentAnnotation.value();
                        // 1.3.4.2.3.3 构造 BeanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition();
                        if(aClass.isAnnotationPresent(Scope.class)){
                            Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(scope.value());
                        }else{
                            beanDefinition.setScope("singleton");
                        }
                        beanDefinition.setClazz(aClass);
                        // 1.3.4.2.3.4 把 BeanDefinition 存储到 map
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object getBean(String beanName){
        // 1. 判断Bean对象是否存在 map
        if (beanDefinitionMap.containsKey(beanName)){
            // 1.1 获取 BeanDefinition
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 1.2 判断该 Bean 的作用域是否为单列
            if(beanDefinition.getScope().equals("singleton")){
                // 1.2.1 直接从单列池中获取 Bean
                return singletonObjects.get(beanName);
            }else {
                // 1.2.2 如果是原型 bean 直接创建新的对象
                return createBean(beanName, beanDefinitionMap.get(beanName));
            }
        }
        // 2. 不存在对应的 Bean，抛出异常
        throw new IllegalArgumentException("不存在对应的 Bean");
    }
}